// Filename:    fifo.v
// Author:      Cody Cziesler
// Description: A configurable FIFO
//

`include "include.v"

module fifo #(parameter FIFO_WIDTH = 16,
                        FIFO_DEPTH = 4,
                        FIFO_LOG2_DEPTH = 2)
(
  input wire [FIFO_WIDTH-1:0] din,
  input wire                  clk,
  input wire                  rst_n,
  input wire                  pop,
  input wire                  push,
  output reg [FIFO_WIDTH-1:0] dout
);

reg [FIFO_WIDTH-1:0] mem [FIFO_DEPTH-1:0];
reg [FIFO_LOG2_DEPTH-1:0] nxt_push_ptr;
reg [FIFO_LOG2_DEPTH-1:0] nxt_pop_ptr;

integer i;

always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    nxt_pop_ptr  <= 1'b0;
    nxt_push_ptr <= 1'b0;
  end else begin
    if (pop) begin
      // Increment pop_ptr
      if (nxt_pop_ptr == FIFO_DEPTH -1) begin 
        nxt_pop_ptr <= 1'b0;
      end else begin
        nxt_pop_ptr <= nxt_pop_ptr + 1'b1;
      end
    end
    if (push) begin
      // Increment push_ptr
      if (nxt_push_ptr == FIFO_DEPTH - 1) begin
        nxt_push_ptr <= 1'b0;
      end else begin
        nxt_push_ptr <= nxt_push_ptr + 1'b1;
      end
    end   
  end 
end

// Always block for pop, push, empty, full, nxt_pop_ptr, nxt_push_ptr
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    for (i = 0; i < FIFO_DEPTH; i=i+1) begin
      mem[i] <= {FIFO_WIDTH{1'b0}};
    end
    dout <= {FIFO_WIDTH{1'b0}};
  end else begin
    if (pop) begin
      // Read memory
      dout <= mem[nxt_pop_ptr];
    end else if (push) begin
      // Write memory
      mem[nxt_push_ptr] <= din;
    end // if(pop)...else if(push)
  end // if(!rst_n)...else
end // always

endmodule
